package com.ygqh.baby.redis;

import com.foxinmy.weixin4j.util.StringUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Transaction;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Set;

public class RedisDao {

    protected Logger log = LoggerFactory.getLogger(getClass());
    private JedisSentinelPool jedisPool;
    private RedisSlaveDao redisSlaveDao;
    private int minIdle;
    private int maxIdle;
    private int maxActive;
    private int maxWait;
    private Boolean testOnBorrow;
    private Boolean testOnReturn;

    private int port;
    private int timeout;
    private String host;
    private String password;

    private String masterName;
    private Set<String> sentinels;


    /**
     * initPoolConfig
     *
     * @return
     */
    private GenericObjectPoolConfig initPoolConfig() {
        GenericObjectPoolConfig jedisPoolConfig = new GenericObjectPoolConfig();
        // 控制一个pool最多有多少个可用的的jedis实例
        jedisPoolConfig.setMaxTotal(maxActive);
        // 最大能够保持空闲状态的对象数
        jedisPoolConfig.setMaxIdle(maxIdle);
        // 超时时间
        jedisPoolConfig.setMaxWaitMillis(maxWait);
        // 在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
        jedisPoolConfig.setTestOnBorrow(false);
        // 在还会给pool时，是否提前进行validate操作
        jedisPoolConfig.setTestOnReturn(false);
        return jedisPoolConfig;
    }

    @PostConstruct
    public void before() {
        GenericObjectPoolConfig jedisPoolConfig = this.initPoolConfig();
        jedisPool = new JedisSentinelPool(masterName, sentinels, jedisPoolConfig, password);
    }

    /**
     * 监控key
     */
    public void watch(String keys) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.watch(keys);
        } finally {
            jedis.close();
        }
    }

    /**
     * 开启事务
     */
    public Transaction multi() {
        Jedis jedis = jedisPool.getResource();
        Transaction tx = null;
        try {
            tx = jedis.multi();
        } finally {
            jedis.close();
        }
        return tx;
    }

    /**
     * get value from redis
     *
     * @param key
     * @return
     */
    public byte[] get(byte[] key) {
        byte[] value = null;
        try {
            value = redisSlaveDao.get(key);
        } catch (Exception e) {
            log.error("redisSlaveDao.get() error. -> {}", e.getMessage(), e);
        }
        if (value == null) {
            Jedis jedis = jedisPool.getResource();
            try {
                value = jedis.get(key);
            } finally {
                jedis.close();
            }
        }
        return value;
    }

    /**
     * set
     *
     * @param key
     * @param value
     * @return
     */
    public byte[] set(byte[] key, byte[] value) {
        Jedis jedis = jedisPool.getResource();

        try {
            jedis.set(key, value);
        } finally {
            jedis.close();
        }
        return value;
    }

    /**
     * set 字符串
     *
     * @param key
     * @param value
     * @return
     */
    public String set(String key, String value, int expire) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.set(key, value);
            if (expire != 0) {
                jedis.expire(key, (expire / 1000));
            }
        } finally {
            jedis.close();
        }
        return value;
    }

    /**
     * 注意：有效期为long值是传的失效日期的时间戳
     *
     * @param key
     * @param value
     * @param unixTime
     * @return String    返回类型
     * @throws
     * @Title: set
     * @author （guohao）
     * @date 2018年6月5日 下午5:01:15
     * @version V1.0
     */
    public String set(String key, String value, long unixTime) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.set(key, value);
            if (unixTime != 0) {
                jedis.expireAt(key, (unixTime / 1000));
            }
        } finally {
            jedis.close();
        }
        return value;
    }

    public byte[] set(byte[] key, byte[] value, long unixTime) {
        Jedis jedis = jedisPool.getResource();
        try {
            String set = jedis.set(key, value);
            if (unixTime != 0) {
                Long expireAt = jedis.expireAt(key, (unixTime / 1000));
                System.err.println(expireAt);
            }
        } finally {
            jedis.close();
        }
        return value;
    }

    public String hget(String key, String field) {
        String value = null;
        try {
            value = redisSlaveDao.hget(key, field);
        } catch (Exception e) {
            log.error("redisSlaveDao.hget({},{}) error. -> {}", key, field, e.getMessage(), e);
        }
        if (StringUtils.isEmpty(value)) {
            Jedis jedis = jedisPool.getResource();
            try {
                value = jedis.hget(key, field);
            } finally {
                jedis.close();
            }
        }
        return value;
    }

    public List<String> hget(String key) {

        List<String> value = null;
        try {
            value = redisSlaveDao.hget(key);
        } catch (Exception e) {
            log.error("redisSlaveDao.hget({}) error. -> {}", key, e.getMessage(), e);
        }
        if (CollectionUtils.isEmpty(value)) {
            Jedis jedis = jedisPool.getResource();
            try {
                value = jedis.hvals(key);
            } finally {
                jedis.close();
            }
        }
        return value;
    }

    public Long hdel(String key, String... fields) {
        Jedis jedis = jedisPool.getResource();
        Long hdel;
        try {
            hdel = jedis.hdel(key, fields);
        } finally {
            jedis.close();
        }
        return hdel;
    }

    public Long hset(String key, String field, String value) {
        Jedis jedis = jedisPool.getResource();
        Long hset;
        try {
            hset = jedis.hset(key, field, value);
        } finally {
            jedis.close();
        }
        return hset;
    }

    public void hset(String key, String field, String value, long timestamp) {
        Jedis jedis = jedisPool.getResource();
        try {
            Pipeline p = jedis.pipelined();
            p.hset(key, field, value);
            p.expireAt(key, timestamp);
            p.sync();
        } finally {
            jedis.close();
        }
    }

    public void hset(byte[] key, byte[] field, byte[] value, int seconds) {
        Jedis jedis = jedisPool.getResource();
        Pipeline p = jedis.pipelined();
        p.hset(key, field, value);
        p.expire(key, seconds);
        p.sync();
        jedis.close();

    }

    public void hset(byte[] key, byte[] field, byte[] value, Long timestamp) {
        Jedis jedis = jedisPool.getResource();
        Pipeline p = jedis.pipelined();
        p.hset(key, field, value);
        p.expireAt(key, timestamp);
        p.sync();
        jedis.close();

    }


    public List<String> hmget(String key, String... field) {
        List<String> hmget = null;
        try {
            hmget = redisSlaveDao.hmget(key, field);
        } catch (Exception e) {
            log.error("redisSlaveDao.hmget({},{}) error. -> {}", key, StringUtil.join(field, ",".charAt(0)), e.getMessage(), e);
        }
        if (CollectionUtils.isEmpty(hmget)) {
            Jedis jedis = jedisPool.getResource();
            try {
                hmget = jedis.hmget(key, field);
            } finally {
                jedis.close();
            }
        }

        return hmget;
    }

    /**
     * set 字符串
     */
    public String get(String key) {
        String value = null;
        try {
            value = redisSlaveDao.get(key);
        } catch (Exception e) {
            log.error("redisSlaveDao.get({}) error. -> {}", key, e.getMessage(), e);
        }
        if (StringUtils.isBlank(value)) {
            Jedis jedis = jedisPool.getResource();
            try {
                value = jedis.get(key);
            } finally {
                jedis.close();
            }
        }

        return value;
    }

    /**
     * set
     *
     * @param key
     * @param value
     * @param expire
     * @return
     */
    public byte[] set(byte[] key, byte[] value, int expire) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.set(key, value);
            if (expire != 0) {
                jedis.expire(key, (expire / 1000));
            }
        } finally {
            jedis.close();
        }
        return value;
    }

    /**
     * del
     *
     * @param key
     */
    public void del(byte[] key) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.del(key);
        } finally {
            jedis.close();
        }
    }

    public void del(String... key) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.del(key);
        } finally {
            jedis.close();
        }
    }

    /**
     * Redis SREM命令用于从所述一组保存在键中删除指定的元素。如果成员不存在，则命令返回0，如果没有设置在关键存储的值，则返回一个错误。
     *
     * @param keys
     * @param key  入参
     * @return void    返回类型
     * @throws
     * @Title: srem
     * @author （guohao）
     * @date 2018年6月28日 下午2:17:48
     * @version V1.0
     */
    public void srem(String keys, String key) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.srem(keys, key);
        } finally {
            jedis.close();
        }
    }

    /**
     * Redis SADD命令用于添加成员设置保存在key。如果成员已经存在，那么它忽略。如果键不存在，那么新的集合创建和成员被添加进去。如果没有设置储存在键的值，则返回一个错误。
     *
     * @param keys
     * @param key  入参
     * @return void    返回类型
     * @throws
     * @Title: sadd
     * @author （guohao）
     * @date 2018年6月28日 下午2:18:54
     * @version V1.0
     */
    public void sadd(String keys, String key) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.sadd(keys, key);
        } finally {
            jedis.close();
        }
    }

    /**
     * flush
     */
    public void flushDB() {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.flushDB();
        } finally {
            jedis.close();
        }
    }

    /**
     * size
     */
    public Long dbSize() {
        Long dbSize = 0L;
        Jedis jedis = jedisPool.getResource();
        try {
            dbSize = jedis.dbSize();
        } finally {
            jedis.close();
        }
        return dbSize;
    }

    /**
     * keys
     */
    public Set<byte[]> keys(String pattern) {
        Set<byte[]> keys = null;
        Jedis jedis = jedisPool.getResource();
        try {
            keys = jedis.keys(pattern.getBytes());
        } finally {
            jedis.close();
        }
        return keys;
    }

    public Set<String> keysStr(String pattern) {
        Set<String> keys;
        Jedis jedis = jedisPool.getResource();
        try {
            keys = jedis.keys(pattern);
        } finally {
            jedis.close();
        }
        return keys;
    }

    public int getMinIdle() {
        return minIdle;
    }

    public void setMinIdle(int minIdle) {
        this.minIdle = minIdle;
    }

    public int getMaxIdle() {
        return maxIdle;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }

    public int getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
    }

    public int getMaxWait() {
        return maxWait;
    }

    public void setMaxWait(int maxWait) {
        this.maxWait = maxWait;
    }

    public Boolean getTestOnBorrow() {
        return testOnBorrow;
    }

    public void setTestOnBorrow(Boolean testOnBorrow) {
        this.testOnBorrow = testOnBorrow;
    }

    public Boolean getTestOnReturn() {
        return testOnReturn;
    }

    public void setTestOnReturn(Boolean testOnReturn) {
        this.testOnReturn = testOnReturn;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getMasterName() {
        return masterName;
    }

    public void setMasterName(String masterName) {
        this.masterName = masterName;
    }

    public RedisSlaveDao getRedisSlaveDao() {
        return redisSlaveDao;
    }

    public void setRedisSlaveDao(RedisSlaveDao redisSlaveDao) {
        this.redisSlaveDao = redisSlaveDao;
    }

    public Set<String> getSentinels() {
        return sentinels;
    }

    public void setSentinels(Set<String> sentinels) {
        this.sentinels = sentinels;
    }

    public JedisSentinelPool getJedisPool() {
        return jedisPool;
    }
}
